package com.github.cdisk.xtrans.component;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.cdisk.xtrans.annotiation.Attribute;
import com.github.cdisk.xtrans.annotiation.Component;
import com.github.cdisk.xtrans.annotiation.ConnectLine;
import com.github.cdisk.xtrans.annotiation.DefaultLine;
import com.github.cdisk.xtrans.core.BaseComponent;
import com.github.cdisk.xtrans.core.ConfigFile;
import com.github.cdisk.xtrans.core.GlobalVariables;
import com.github.cdisk.xtrans.exception.BuildException;
import com.github.cdisk.xtrans.exception.NoVariableNameException;

@Component(value="HTTP请求", icon="drive_web.png", group="常用元件")
public class HttpClient extends BaseComponent {

	private static final Logger LOGGER = Logger.getLogger(HttpClient.class);
	
	private static final ObjectMapper OBJECTMAPPER = new ObjectMapper();

	public static enum IN_TYPE {formdata, jsonRaw};

	public static enum OUT_TYPE {text, json};
	
	public static enum METHOD_TYPE {GET, POST, PUT, PUSH, DELETE, PACTH};

	/** 元件逐条执行过程 */
	@DefaultLine
	@ConnectLine(value="逐条处理")
	@Attribute(value="每条数据的处理过程", group="执行")
	protected BaseComponent proccess;

	/** 请求连接地址 */
	@Attribute(value="请求地址", group="请求")
	protected String url = "";

	/** HEADER列表 */
	@Attribute(value="请求属性", group="请求")
	protected String requestProperty;

	/** 连接超时时间 */
	@Attribute(value="连接超时时间", group="请求")
	protected Integer connectTimeout = 30000;

	/** 读取超时时间 */
	@Attribute(value="读取超时时间", group="请求")
	protected Integer readTimeout = 30000;

	/** 请求类型 */
	@Attribute(value="请求类型", group="请求")
	protected METHOD_TYPE method = METHOD_TYPE.GET;

	/** 请求输入模式 */
	@Attribute(value="请求输入模式", group="请求")
	protected IN_TYPE inType = IN_TYPE.formdata;

	/** 结果输出模式 */
	@Attribute(value="结果输出模式", group="请求")
	protected OUT_TYPE outType = OUT_TYPE.json;

	/** 请求返回参数的名称 */
	@Attribute(value="结果输出名称", group="请求")
	protected String resultName = "result";

	/** 请求参数字段 */
	@Attribute(value="请求发送的字段名称，换行分隔；不填代表所有字段。", group="请求")
	protected String[] params = null;

	/** 请求字符集类型 */
	@Attribute(value="请求字符集类型", group="请求")
	protected String charset = "utf-8";

	private String _proccess;


	@Override
	public void build(ConfigFile config, GlobalVariables variables) throws BuildException {
		super.build(config, variables);

		try {
			url = variables.replaceVariables(url); // 替换变量
			if (StringUtils.isEmpty(url)) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[url]内容。");
			}
			if (method == null) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[method]内容。");
			}
			resultName = variables.replaceVariables(resultName); // 替换变量
			if (StringUtils.isEmpty(resultName)) {
				throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")未找到[resultName]内容。");
			}
	
			requestProperty = variables.replaceVariables(requestProperty); // 替换变量
			charset = variables.replaceVariables(charset); // 替换变量
		} catch (NoVariableNameException e) {
			throw new BuildException(this.getClass().getSimpleName() + "(" +  this.id + ")" + e.getMessage());
		}

		/* 检测逐条执行过程 */
		if (_proccess != null && _proccess.length() != 0) {
			proccess = config.findComponentById(_proccess);
			if (proccess == null) throw new BuildException(this.getClass().getName() + "(" +  this.id + ")未找到语句[proccess]说明：" + _proccess);
		}
	}

	@Override
	public int execute(Map<String, Object> input) {
		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
			conn.setRequestMethod(method.name());
			conn.setDoOutput(true);
			conn.setDoInput(true);
			if (requestProperty != null && requestProperty.length() > 0) {
				/* 处理额外的请求属性。 */
				for (String property : requestProperty.split("\r?\n")) {
					String[] keyValue = property.split(":\\s?", 2);
					if (keyValue.length == 2) {
						conn.addRequestProperty(keyValue[0], keyValue[1]);
					}
				}
			}

			/* 输入请求参数处理 */
			String postData = "";
			if (inType == IN_TYPE.formdata) {
				/* 表单提交模式，把上一步的参数以表单参数的形式进行请求。 */
				conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");

				if (params != null && params.length > 0) {
					/* 限定字段传参，此处与else有重复代码。 */
					for (String key : params) {
						Object value = GlobalVariables.getInputValue(key, input);
						String[] keySplit = key.split("\\.");
						key = keySplit[keySplit.length - 1];
						if (value != null && value.getClass().isArray()) {
							int len = Array.getLength(value);
							for (int i = 0; i < len; i++) {
								Object item = Array.get(value, i);
								postData += key + "=" + (item != null ? item : "") + "&";
							}
						} else {
							postData += key + "=" + (value != null ? value : "") + "&";
						}
					}
				} else {
					/* 不限定字段，传所有字段入参，此处与if有重复代码。 */
					for (String key : input.keySet()) {
						if (key.startsWith("$")) {
							continue; // 跳过自带参数
						}
						Object oVal = input.get(key);
						if (oVal != null && oVal.getClass().isArray()) {
							int len = Array.getLength(oVal);
							for (int i = 0; i < len; i++) {
								Object item = Array.get(oVal, i);
								postData += key + "=" + (item != null ? item : "") + "&";
							}
						} else {
							postData += key + "=" + (oVal != null ? oVal : "") + "&";
						}
					}
				}
			} else if (inType == IN_TYPE.jsonRaw) {
				/* JSON提交模式，把上一步的参数以JSONOBJECT的形式进行请求。 */
				HashMap<String, Object> paramsMap = new HashMap<String, Object>();
				if (params != null && params.length > 0) {
					/* 限定字段传参 */
					for (String key : params) {
						Object value = GlobalVariables.getInputValue(key, input);
						String[] keySplit = key.split("\\.");
						key = keySplit[keySplit.length - 1];
						paramsMap.put(key, value);
					}
				} else {
					/* 不限定字段，传所有字段入参 */
					for (String key : input.keySet()) {
						if (key.startsWith("$")) {
							continue; // 跳过自带参数
						}
						paramsMap.put(key, input.get(key));
					}
				}
				conn.setRequestProperty("Content-Type", "application/json");
				postData = OBJECTMAPPER.writeValueAsString(paramsMap);
			}
			conn.getOutputStream().write(postData.getBytes());
			BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
			String result = "";
			String line = null;
			while ( (line = reader.readLine()) != null ) {
				result += line;
			}
			conn.disconnect();

			/* 返回结果输出处理 */
			Map<String, Object> __map = new HashMap<String, Object>();
			if (outType == OUT_TYPE.text) {
				/* 执行过程处理 */
				if (this.proccess != null) {
					__map.put("$parent", input);
					__map.put(resultName, result);
					this.proccess.execute(__map);
				}
			} else {
				if (result.startsWith("[")) {
					List<?> list = OBJECTMAPPER.readValue(result, List.class);
					/* 执行过程处理 */
					if (this.proccess != null) {
						for (Object object : list) {
							__map.put("$parent", input);
							__map.put(resultName, object);
							this.proccess.execute(__map);
						}
					}
				} else {
					Map<?, ?> map = OBJECTMAPPER.readValue(result, Map.class);
					/* 执行过程处理 */
					if (this.proccess != null) {
						__map.put("$parent", input);
						__map.put(resultName, map);
						this.proccess.execute(__map);
					}
				}
			}
			/* 执行成功处理 */
			if (this.success != null) {
				__map.put("$parent", input);
				__map.put(resultName, result);
				this.success.execute(__map);
			}
			LOGGER.info("执行请求:" + this.id + ", 执行成功.");
			return EXECUTE_SUCCESS;
		} catch (Exception e) {
			/* 执行错误处理 */
			Map<String, Object> __map = new HashMap<String, Object>();
			if (this.error != null) {
				__map.put("$errorMessage", e.getMessage());
				this.error.execute(input);
			}
			e.printStackTrace();
			LOGGER.info("执行请求:" + this.id + ", 执行错误, " + e.getClass().getSimpleName() + ": " + e.getMessage());
			return EXECUTE_ERROR;
		}
	}

	public BaseComponent getProccess() {
		return proccess;
	}

	public void setProccess(BaseComponent proccess) {
		this.proccess = proccess;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getRequestProperty() {
		return requestProperty;
	}

	public void setRequestProperty(String requestProperty) {
		this.requestProperty = requestProperty;
	}

	public Integer getConnectTimeout() {
		return connectTimeout;
	}

	public void setConnectTimeout(Integer connectTimeout) {
		this.connectTimeout = connectTimeout;
	}

	public Integer getReadTimeout() {
		return readTimeout;
	}

	public void setReadTimeout(Integer readTimeout) {
		this.readTimeout = readTimeout;
	}

	public METHOD_TYPE getMethod() {
		return method;
	}

	public void setMethod(METHOD_TYPE method) {
		this.method = method;
	}

	public IN_TYPE getInType() {
		return inType;
	}

	public void setInType(IN_TYPE inType) {
		this.inType = inType;
	}

	public OUT_TYPE getOutType() {
		return outType;
	}

	public void setOutType(OUT_TYPE outType) {
		this.outType = outType;
	}

	public String getResultName() {
		return resultName;
	}

	public void setResultName(String resultName) {
		this.resultName = resultName;
	}

	public String[] getParams() {
		return params;
	}

	public void setParams(String[] params) {
		this.params = params;
	}

	public String getCharset() {
		return charset;
	}

	public void setCharset(String charset) {
		this.charset = charset;
	}

}
